package shlib

import (
	"errors"
	"fmt"
	"strings"
)

const (
	// 公钥类型
	KeyType_Public = "PUB"

	// 私钥类型
	KeyType_Private = "PRV"

	// 对称密钥类型
	KeyType_Secret = "SEC"
)

/**
 * 抽象密钥对象
 */
type IKey interface {
	// 算法
	Algo() string

	// 类型
	Type() string

	// 导出密钥数据，可以后续被用来恢复密钥
	ExportToXBuf(xb *XBuf)
	Export() []byte
	ExportTo(dst []byte) int

	// 判断两个密钥是否相等
	Equals(other IKey) bool
}

/**
 * 抽象公钥对象
 */
type IPublicKey interface {
	IKey

	// 验证数据与其签名是否匹配
	Verify(data []byte, signature []byte) bool
}

/**
 * 抽象 ECC 公钥对象
 */
type IEcPublicKey interface {
	IPublicKey
}

/**
 * 抽象私钥对象
 */
type IPrivateKey interface {
	IKey

	// 从本私钥中分离出公钥
	Public() IPublicKey

	// 签名
	Sign(data []byte) []byte
}

/**
 * 抽象 ECC 私钥对象
 */
type IEcPrivateKey interface {
	IPrivateKey

	/*
	 * 遵从 ECDHE 算法，将输入公钥与本私钥相乘，并用 sk 对结果做一次加密，所得结果即为
	 * 待使用的会话密钥的种子。
	 *
	 * 将本私钥对应的公钥和 sk 一并传输给输入公钥对应的私钥持有者，对方可以用同样的方法
	 * 恢复出同样的会话密钥的种子。
	 */
	RecoverKey(pk IPublicKey, sk ISecretKey) ([]byte, error)
}

/**
 * 抽象加解密密钥对象
 */
type ISecretKey interface {
	IKey

	// 加密数据
	EncryptToXBuf(data []byte, xb *XBuf)
	Encrypt(data []byte) []byte

	// 解密数据
	DecryptToXBuf(data []byte, xb *XBuf) error
	Decrypt(data []byte) ([]byte, error)
}

/**
 * 根据指定密钥类型（PUB/PRV/SEC）生成密钥对象的函数。
 * 返回值中包含从 keyData 中读取的字节数。
 */
type KeyFactory func(keyType string, keyData []byte) (IKey, int, error)

// 生成有值的密钥的函数。
// 对于对称算法，生成的是 ISecretKey；对于非对称算法，生成的是 IPrivateKey。
type KeyGenerator func() (IKey, error)

var (
	// Algo -> KeyFactory
	keyFactoryMap map[string]KeyFactory

	// Algo -> KeyGenerator
	keyGeneratorMap map[string]KeyGenerator
)

func errKeyNotFound(alias string) error {
	return errors.New(fmt.Sprintf("Can not find key by alias '%s'.", alias))
}

func errDuplicatedKeyAlias(alias string) error {
	return errors.New(fmt.Sprintf("Duplicated key alias '%s'.", alias))
}

func errKeyAlgorithm(algo string) error {
	return errors.New(fmt.Sprintf("Invalid key algorithm '%s'.", algo))
}

func errKeyType(typ string) error {
	return errors.New(fmt.Sprintf("Invalid key type '%s'.", typ))
}

var errKeyData = errors.New("invalid key data")

var errSecretData = errors.New("invalid secret data")

/**
 * 从 XBUF 中读取数据还原出秘钥对象。
 *
 * 该数据必须是 IKey::Export...() 方法族产生的，其中包含密钥类型和算法信息。
 */
func LoadKeyFromXBuf(xb *XBuf) (IKey, error) {
	keyAlgo, err := xb.GetVStr()
	if err != nil {
		return nil, errKeyData
	}

	keyType, err := xb.GetVStr()
	if err != nil {
		return nil, errKeyData
	}

	kfac := keyFactoryMap[keyAlgo]
	if kfac == nil {
		return nil, errKeyAlgorithm(keyAlgo)
	}

	key, l, err := kfac(keyType, xb.Bytes())
	if err != nil {
		return nil, err
	}
	xb.Ri += l

	return key, nil
}

func LoadKey(keyData []byte) (IKey, error) {
	xb := XBuf{keyData, 0, len(keyData), false}
	return LoadKeyFromXBuf(&xb)
}

/**
 * 从一个 Base58 编码的字符串中还原出密钥对象。
 */
func LoadKeyFromBase58(keyStr string) (IKey, error) {
	keyData, err := Base58Decode(keyStr)
	if err != nil {
		return nil, err
	}
	xb := XBuf{keyData, 0, len(keyData), false}

	return LoadKeyFromXBuf(&xb)
}

func LoadKeyFromBase58Ne(keyStr string) IKey {
	k, err := LoadKeyFromBase58(keyStr)
	if err != nil {
		panic(err)
	}

	return k
}

/**
 * 根据指定算法生成随机密钥。
 */
func GenerateKey(keyAlgo string) (IKey, error) {
	kgen := keyGeneratorMap[strings.ToUpper(keyAlgo)]
	if kgen == nil {
		return nil, errKeyAlgorithm(keyAlgo)
	}

	key, err := kgen()
	if err != nil {
		return nil, err
	}

	return key, nil
}

/**
 * 登记每种算法的 KeyFactory 和 KeyGenerator。
 */
func init() {
	keyFactoryMap = make(map[string]KeyFactory)
	keyFactoryMap["AES-128"] = AesKeyFactory
	keyFactoryMap["ECC-256"] = EccKeyFactory
	keyFactoryMap["ED25519"] = Ed25519KeyFactory

	keyGeneratorMap = make(map[string]KeyGenerator)
	keyGeneratorMap["AES-128"] = AesKeyGenerator
	keyGeneratorMap["ECC-256"] = EccKeyGenerator
	keyGeneratorMap["ED25519"] = Ed25519KeyGenerator
}
